/**
 ******************************************************************************
 * @file    timeouts.c
 * @author  jobs
 * @version v0.00
 * @date    2020-04-18 21:34
 * @brief
 * @note
 *
 ******************************************************************************
 */

#include <stddef.h>
#include "timeouts.h"
#include "def.h"
#include "main.h"

#include "frontled.h"

#define CYCLIC_MAX_TIMEOUT (0x7fffffff)

#define TIME_LESS_THAN(t, compare_to) ((((uint32_t)((t) - (compare_to))) > CYCLIC_MAX_TIMEOUT) ? 1 : 0)

const struct cyclic_timer cyclic_timers[] =
{
	// {charge_poll_interval, charge_manage_poll},
	{led_proc_interval, front_led_proc},
};

const int num_cyclic_timers = ARRAYSIZE(cyclic_timers);

static uint32_t current_timeout_due_time;
static struct sys_timeo sys_timeo_array[ARRAYSIZE(cyclic_timers)];

static void sys_timeout_abs(uint32_t abs_time, sys_timeout_handler handler, void *arg)
{
	uint8_t i;
	struct sys_timeo tmptimeout;

	sys_timeo_array[0].time = abs_time;
	sys_timeo_array[0].h = handler;
	sys_timeo_array[0].arg = arg;

	for (i = 0; i < num_cyclic_timers - 1; ++i)
	{
		if (TIME_LESS_THAN(sys_timeo_array[i].time, sys_timeo_array[i + 1].time))
		{
			return;
		}
		else
		{
			tmptimeout = sys_timeo_array[i];
			sys_timeo_array[i] = sys_timeo_array[i + 1];
			sys_timeo_array[i + 1] = tmptimeout;
		}
	}
}

static void cyclic_timer(void *arg)
{
	uint32_t now;
	uint32_t next_timeout_time;
	const struct cyclic_timer *cyclic = (const struct cyclic_timer *)arg;

	now = get_sysnow();
	cyclic->handler();

	next_timeout_time = (uint32_t)(current_timeout_due_time + cyclic->interval_ms); /* overflow handled by TIME_LESS_THAN macro */
	if (TIME_LESS_THAN(next_timeout_time, now))
	{
		sys_timeout_abs((uint32_t)(now + cyclic->interval_ms), cyclic_timer, arg);
	}
	else
	{
		sys_timeout_abs(next_timeout_time, cyclic_timer, arg);
	}
}

void sys_timeout(uint32_t msecs, sys_timeout_handler handler, void *arg)
{
	uint32_t next_timeout_time;

	next_timeout_time = (uint32_t)(get_sysnow() + msecs); /* overflow handled by TIME_LESS_THAN macro */

	sys_timeout_abs(next_timeout_time, handler, arg);
}

/** Initialize this module */
void sys_timeouts_init(void)
{
	uint8_t i;
	for (i = 0; i < ARRAYSIZE(cyclic_timers); ++i)
	{
		sys_timeout(cyclic_timers[i].interval_ms, cyclic_timer, (void *)&cyclic_timers[i]);
	}
}

void sys_check_timeouts(void)
{
	uint32_t now;

	now = get_sysnow();

	do
	{
		if (TIME_LESS_THAN(now, sys_timeo_array[0].time))
		{
			return;
		}

		/* Timeout has expired */
		current_timeout_due_time = sys_timeo_array[0].time;
		if (sys_timeo_array[0].h != NULL)
		{
			sys_timeo_array[0].h(sys_timeo_array[0].arg);
		}

		/* Repeat until all expired timers have been called */
	} while (1);
}

/******************* (C) lemonboard *****END OF FILE****/
